﻿//-------------------------------------------------------
// Copyright (c) 
// All rights reserved.
//
// File Name: CTimer.h
// File Des: timer封装
// File Summary: 
/*    CUITimer UiTimer;
      CTPTimer TpTimer;
      UiTimer.SetTimer((HWND)hwnd1, WM_TIME1, 1500, &cb);
      UiTimer.KillTimer((HWND)hwnd1, WM_TIME1);
      TpTimer.SetTimer((HWND)hwnd2, WM_TIME2, 1000, &cb);
      TpTimer.KillTimer((HWND)hwnd2, WM_TIME2);
      TpTimer.SetTimer(WM_TIME1, 1000, &Timer_CallBack, this);
      TpTimer.SetTimer(1000, &Timer_CallBack, this);
*/
// Cur Version: 1.0
// Author:
// Create Data:2024-4-02
// History:
//     <Author>    <Time>     <Version>    <Des>
//      lzlong    2024-4-2      1.0
//-------------------------------------------------------
#pragma once

#include "CMapT.h"
#include "LinkNodeT.h"

/// <summary>
///   超时接口回调
/// </summary>
class ITimer;      ///< Declare
struct TIMERINFO;  ///< Declare
typedef TIMERINFO* TIMERINFO_PTR; ///< Declare

/// <summary>
/// TimeOut CallBack Interface 需要继承并实现虚函数OnTimer
/// </summary>
typedef class _ITimerCallback
{
public:
    virtual ~_ITimerCallback() {}
    virtual VOID OnTimer(ITimer* lpTimer, TIMERINFO_PTR lpTimerInfo) = 0;
}ITimerCallback, *ITimerCallbackPtr;

/// <summary>
/// CallBack Function Interface Declare
/// </summary>
typedef VOID(CALLBACK* TIMER_CALLBACK)(ITimer* lpTimer, TIMERINFO_PTR lpTimerInfo);

/// <summary>
///   TIMERINFO 结构体
/// </summary>
struct TIMERINFO
{
    HWND              hWnd;
    UINT_PTR          uTimerID;
    ITimerCallbackPtr lpTimerCb;
    TIMER_CALLBACK    lpCallBack;
    PVOID             pvContext;
    UINT              uExcCnt;
    DWORD64           dwCreateTm;
    DWORD64           dwCurExcTm;
};

/// <summary>
/// Timer的纯接口类定义
/// </summary>
class ITimer
{
public:
    virtual ~ITimer() {}

    ///< 设置定时器 类指针回调
    virtual int SetTimer(UINT_PTR uTimerID, UINT nElapse, ITimerCallbackPtr lpTimerCb, PVOID pvContext = NULL) = 0;

    ///< 设置定时器 类指针回调  带窗口句柄ID
    virtual int SetTimer(HWND hWnd, UINT_PTR uTimerID, UINT nElapse, ITimerCallbackPtr lpTimerCb, PVOID pvContext = NULL) = 0;

    ///< 设置定时器 通过类指针回调 直接绑定回调函数   不需要TimerID 用随机TimerID
    virtual int SetTimer(UINT nElapse, const TIMER_CALLBACK& lpCb, PVOID pvContext = NULL) = 0;

    ///< 设置定时器 通过类指针回调
    virtual int SetTimer(UINT_PTR uTimerID, UINT nElapse, const TIMER_CALLBACK& lpCb, PVOID pvContext = NULL) = 0;

    ///< 设置定时器 通过函数回调 带窗口句柄ID
    virtual int SetTimer(HWND hWnd, UINT_PTR uTimerID, UINT nElapse, const TIMER_CALLBACK& lpCb, PVOID pvContext = NULL) = 0;

    ///< 停止定时器 (窗口句柄ID默认为NULL，匹配消息ID即可)
    virtual int KillTimer(UINT_PTR uTimerID) = 0;

    ///< 停止定时器 必须匹配消息ID和窗口句柄ID
    virtual int KillTimer(HWND hWnd, UINT_PTR uTimerID) = 0;

    ///< 通过窗口句柄ID匹配，停止定时器  窗口句柄ID为NULL直接返回
    virtual int KillTimer(HWND hWnd) = 0;

    ///< 删除定时器 通过TIMERINFO指针
    virtual int KillTimer(const TIMERINFO_PTR& lpTimerInfo) = 0;

    ///< 取消当前类的所有定时器
    virtual int KillAllTimer() = 0;
};

/// <summary>
/// UI线程的Timer萃取类，通过WM_TIMER消息实现的，必须在UI线程调用才有效
/// </summary>
class CUITimerTraits : public CLinkNodeT<CUITimerTraits>
{
public:
    typedef typename UINT_PTR TIMER_TYPE; //using TIMER_TYPE = typename UINT_PTR;
public:
    CUITimerTraits() {}
    virtual ~CUITimerTraits() {}

    TIMER_TYPE SetTimer(UINT_PTR uTimerID, UINT nElapse)
    {
        return ::SetTimer(NULL, uTimerID, nElapse, UITimerProc);
    }

    BOOL KillTimer(TIMER_TYPE uIDEvent)
    {
        return ::KillTimer(NULL, uIDEvent);
    }

    static void CALLBACK UITimerProc(HWND hwnd, UINT uMsg, UINT_PTR uRealTimeID, DWORD dwTime)
    {
        CUITimerTraits* pTimerObj = GetHeadNode();
        for (; (uMsg == WM_TIMER) && pTimerObj; pTimerObj = pTimerObj->GetNextNode())
        {
            pTimerObj->OnTimerNotify(uRealTimeID);
        }
    }

    virtual VOID OnTimerNotify(TIMER_TYPE lpTimer) = 0;
};

/// <summary>
/// Thread pool线程的Timer萃取类，通过系统开辟新的线程来实现的，可以在任意线程使用
/// 线程池Timer很神奇，如果当前Timer线程Proc卡顿，系统会自动开辟新的线程来执行Proc，不会阻塞
/// </summary>
class CTPTimerTraits
{
public:
    typedef typename PTP_TIMER TIMER_TYPE; //using TIMER_TYPE = typename PTP_TIMER;
public:
    CTPTimerTraits() {}
    virtual ~CTPTimerTraits() {}

public:
    TIMER_TYPE SetTimer(UINT_PTR uTimerID, UINT nElapse)
    {
        PTP_TIMER lpTimer = CreateThreadpoolTimer(TPTimerProc, this, NULL);
        if (NULL == lpTimer)
        {
            return NULL;
        }

        ULARGE_INTEGER ulRelativeStartTime;
        ulRelativeStartTime.QuadPart = -(LONGLONG)(nElapse * 10000); // start in x ms
        FILETIME ftRelativeStartTime;
        ftRelativeStartTime.dwHighDateTime = ulRelativeStartTime.HighPart;
        ftRelativeStartTime.dwLowDateTime = ulRelativeStartTime.LowPart;
        SetThreadpoolTimer(
            lpTimer,
            &ftRelativeStartTime,
            nElapse, // Triggers every xx milliseconds
            0
        );
        return lpTimer;
    }

    BOOL KillTimer(TIMER_TYPE lpTimer)
    {
        CloseThreadpoolTimer(lpTimer);
        return TRUE;
    }

    static VOID CALLBACK TPTimerProc(
        PTP_CALLBACK_INSTANCE pInstance,
        PVOID pvContext,
        PTP_TIMER lpTimer)
    {
        CTPTimerTraits* pTimerObj = (CTPTimerTraits*)pvContext;
        do
        {
            if (!pTimerObj)
                break;

            pTimerObj->OnTimerNotify(lpTimer);
        } while (FALSE);
    }

    virtual VOID OnTimerNotify(TIMER_TYPE lpTimer) = 0;
};

/// <summary>
/// Timer的具体实现类和接口定义
/// </summary>
template<class Traits>
class CTimerImpl
    : public Traits
    , public CMapT<typename Traits::TIMER_TYPE, TIMERINFO>
    , public ITimer
{
public:
    typedef typename Traits::TIMER_TYPE TIMER_TYPE;
    typedef CMapT<TIMER_TYPE, TIMERINFO> __baseClass;

public:
    CTimerImpl() {}

    ~CTimerImpl()
    {
        KillAllTimer();
    }

    /// -------------------------------------------------
    /// @brief 设置定时器
    /// @param[in]  uTimerID 设置时间ID  nElapse 时间间隔(MS)  lpTimerCb回调类，需要用户实现 pvContext顺带参数
    /// @return 成功0  失败-1
    int SetTimer(UINT_PTR uTimerID, UINT nElapse, ITimerCallbackPtr lpTimerCb, PVOID pvContext = NULL) override
    {
        return SetTimer(NULL, uTimerID, nElapse, lpTimerCb, NULL, pvContext);
    }

    ///< 设置定时器 通过类指针回调 带窗口句柄ID
    int SetTimer(HWND hWnd, UINT_PTR uTimerID, UINT nElapse, ITimerCallbackPtr lpTimerCb, PVOID pvContext = NULL) override
    {
        return SetTimer(hWnd, uTimerID, nElapse, lpTimerCb, NULL, pvContext);
    }

    ///< 设置定时器 通过类指针回调 直接绑定回调函数  不需要TimerID 用随机TimerID
    int SetTimer(UINT nElapse, const TIMER_CALLBACK& lpCb, PVOID pvContext = NULL) override
    {
        srand((unsigned int)GetTickCount());
        return SetTimer(NULL, rand(), nElapse, NULL, lpCb, pvContext);
    }

    ///< 设置定时器 通过函数回调
    int SetTimer(UINT_PTR uTimerID, UINT nElapse, const TIMER_CALLBACK& lpCb, PVOID pvContext = NULL) override
    {
        return SetTimer(NULL, uTimerID, nElapse, NULL, lpCb, pvContext);
    }

    ///< 设置定时器 通过函数回调 带窗口句柄ID
    int SetTimer(HWND hWnd, UINT_PTR uTimerID, UINT nElapse, const TIMER_CALLBACK& lpCb, PVOID pvContext = NULL) override
    {
        return SetTimer(hWnd, uTimerID, nElapse, NULL, lpCb, pvContext);
    }

    ///< 停止定时器 (窗口句柄ID默认为NULL，匹配消息ID即可)
    int KillTimer(UINT_PTR uTimerID) override
    {
        return KillTimer(NULL, uTimerID);
    }

    ///< 停止定时器 必须匹配消息ID和窗口句柄ID
    int KillTimer(HWND hWnd, UINT_PTR uTimerID) override
    {
        int iErr = -1;
        do
        {
            Misc::Mutex::Lock lock(__baseClass::m_mutex);
            for (auto it = __baseClass::m_Map.begin();
                it != __baseClass::m_Map.end();
                it ++)
            {
                if (((hWnd && it->second.hWnd == hWnd) || !hWnd) // 存在句柄且句柄相等
                    && it->second.uTimerID == uTimerID)
                {
                    Traits::KillTimer(it->first);
                    __baseClass::RemoveKey(it->first);
                    iErr = 0;
                    break;
                }
            }
        } while (false);
        return iErr;
    }

    ///< 通过窗口句柄ID匹配，停止定时器  窗口句柄ID为NULL直接返回
    int KillTimer(HWND hWnd) override
    {
        int iErr = -1;
        do
        {
            if (!hWnd)
                break;
            Misc::Mutex::Lock lock(__baseClass::m_mutex);
            for (auto it = __baseClass::m_Map.begin();
                it != __baseClass::m_Map.end();
                )
            {
                if (it->second.hWnd == hWnd)
                {
                    Traits::KillTimer(it->first);
                    __baseClass::RemoveKey(it->first);
                    it = __baseClass::m_Map.begin();
                    iErr = 0;
                }
                else
                {
                    it++;
                }
            }
        } while (false);
        return iErr;
    }

    ///< 删除定时器 通过TIMERINFO指针
    int KillTimer(const TIMERINFO_PTR& lpTimerInfo) override
    {
        if (!lpTimerInfo)
            return -1;
        return KillTimer(lpTimerInfo->hWnd, lpTimerInfo->uTimerID);
    }

    ///< 删除当前类对象所有定时器
    int KillAllTimer() override
    {
        int iErr = -1;
        do
        {
            Misc::Mutex::Lock lock(__baseClass::m_mutex);
            for (auto it = __baseClass::m_Map.begin();
                it != __baseClass::m_Map.end();
                )
            {
                Traits::KillTimer(it->first);
                __baseClass::RemoveKey(it->first);
                it = __baseClass::m_Map.begin();
                iErr = 0;
            }
        } while (false);
        return iErr;
    }

protected:
    /// <summary>
    ///  Implement
    /// </summary>
    /// <param name="hWnd"> 窗口句柄ID </param>
    /// <param name="uTimerID"> 消息ID </param>
    /// <param name="nElapse"> 时间间隔(ms) </param>
    /// <param name="lpTimerCb"> 类回调指针 </param>
    /// <param name="lpCb"> 函数回调指针 </param>
    /// <returns> suc:0 fail:-1 </returns>
    int SetTimer(HWND hWnd, UINT_PTR uTimerID, UINT nElapse, ITimerCallbackPtr lpTimerCb, const TIMER_CALLBACK& lpCbFun, PVOID pvContext)
    {
        int iErr = -1;
        do
        {
            KillTimer(hWnd, uTimerID);
            TIMER_TYPE uRealTimeID = Traits::SetTimer(uTimerID, nElapse);
            if (0 == uRealTimeID)
            {
                break;
            }
            TIMERINFO TimeInfo = { hWnd, uTimerID, lpTimerCb, lpCbFun, pvContext, 0, GetTickCount64(), GetTickCount64() }; // 如果pTimerCb为NULL 就用类默认回调
            if (!__baseClass::AddKey(uRealTimeID, TimeInfo))
            {
                break;
            }
            iErr = 0;
        } while (false);
        return iErr;
    }

protected:
    /// <summary>
    /// TimeOut Callback
    /// </summary>
    /// <param name="lpTimer"> ID </param>
    /// <returns></returns>
    VOID OnTimerNotify(TIMER_TYPE lpTimer) override
    {
        BOOL bKeyExist = FALSE;
        TIMERINFO TimeInfo;
        do
        {
            Misc::Mutex::Lock lock(__baseClass::m_mutex);
            if (!__baseClass::GetObjByKey(lpTimer, TimeInfo))
            {
                break;
            }
            TIMERINFO& obj = __baseClass::GetObjByKey(lpTimer);
            obj.uExcCnt++;  // Add Excute Count
            obj.dwCurExcTm = GetTickCount64(); // update Current Task Excute time
            bKeyExist = TRUE;
        } while (FALSE);

        if (bKeyExist)
        {
            if (TimeInfo.lpTimerCb)
            {
                TimeInfo.lpTimerCb->OnTimer(this, &TimeInfo);
            }
            if (TimeInfo.lpCallBack)
            {
                TimeInfo.lpCallBack(this, &TimeInfo);
            }
        }
    }

protected:
    ITimerCallbackPtr     m_lpTimerCb;   ///< 回调对象指针
};

/// <summary>
/// Timer对外使用接口定义
/// </summary>
typedef CTimerImpl<CUITimerTraits>     CUITimer;  ///< UI线程Timer 当前线程需要有消息循环才能生效
typedef CTimerImpl<CTPTimerTraits>     CTPTimer;  ///< ThreadPool Timer 系统会自动开辟新的线程执行Timer